package exam;


import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class UserDaoImpl implements UserDao{

    /**
     * 创建表
     */
    @Override
    public void createTable() {

        Connection conn = null;
        Statement sm = null;

        Class userClass = User.class;


        try {
            //加载数据库驱动
            Class.forName(Const.DRIVER);
            //建立数据库连接
            conn = DriverManager.getConnection(
                    Const.URL_TEST,
                    Const.USERNAME,
                    Const.PASSWORD
            );
            //创建表的sql语句
            String sql = "create table if not exists TB_USER(userno INT PRIMARY KEY,userName VARCHAR(20) NOT NULL,`password` VARCHAR(20),job VARCHAR(20),sal INT)";

            //执行sql语句
            sm = conn.createStatement();
            sm.executeUpdate(sql);
            System.out.println("表创建成功");
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            ConnectionTool.closed(conn, sm);
        }
    }

    /**
     * 插入一个用户
     * @param user
     */
    @Override
    public void insert(User user) {
        Connection conn = null;
        PreparedStatement ps = null;
        //创建需要添加到表中的属性的索引列表
        List<Integer> toAddFieldIndexList = new ArrayList<>();

        String tableName = "";
        String fieldName = "";

        //获取对象的Class类型
        Class userClass = user.getClass();

        //判断当前类对象对应的类中是否存在Table这个注解
        if (userClass.isAnnotationPresent(Table.class)){
            //如果有，则就把这个注解的value值作为表名
            Table table = (Table) userClass.getDeclaredAnnotation(Table.class);
            tableName = table.value();
        }else{
            //获取当前对象的类名直接作为sql语句的表名
            tableName = userClass.getSimpleName().toLowerCase();
        }

        try {
            //加载数据库驱动
            Class.forName(Const.DRIVER);
            //建立数据库连接
            conn = DriverManager.getConnection(
                    Const.URL_TEST,
                    Const.USERNAME,
                    Const.PASSWORD
            );

            StringBuffer sb = new StringBuffer("insert into "+tableName+"(");

            //获取当前对象的类中所有属性数组
            Field[] fields = userClass.getDeclaredFields();

            //遍历所有的属性
            for (int i = 0; i < fields.length; i++) {
                Field currentField = fields[i];

                //需要先判断主键里的那个字段是否为自增长
                if (currentField.isAnnotationPresent(PrimaryKey.class)){
                    PrimaryKey primaryKeyAnnotation = currentField.getDeclaredAnnotation(PrimaryKey.class);
                    if (primaryKeyAnnotation.autoIncrement()){
                        //如果是的话就跳出当前循环，执行下一个循环
                        continue;
                    }
                }

                //无论是否为主键都是需要添加的列
                toAddFieldIndexList.add(i);

                //再判断是否有Column注解，如果有的话就要读取Column注解中的value值作为要添加的字段
                if (currentField.isAnnotationPresent(Column.class)){
                    Column columnAnnotation = currentField.getDeclaredAnnotation(Column.class);
                    fieldName = columnAnnotation.value();
                }else {
                    //如果没有就直接读取属性名作为要添加的字段名
                    fieldName = currentField.getName().toLowerCase();
                }

                sb.append(fieldName + (i==fields.length-1?")":","));
            }
            sb.append(" values(");

            //接下来处理的是要添加的属性，应该有多少个问号，这里可以用list存储该属性在属性列表里的索引值
            for (int i = 1; i <= toAddFieldIndexList.size(); i++) {
                sb.append(i==toAddFieldIndexList.size()?"?)":"?,");
            }

            //对sql语句进行预处理
            ps = conn.prepareStatement(sb.toString());
            //System.out.println("执行的 SQL 语句: " + sb.toString());

            //设置sql语句里的问号值
            for (int i = 0; i < toAddFieldIndexList.size(); i++) {
                //获取属性数组中索引值为toAddFieldIndexList中的第i个索引值
                Field currentField = fields[toAddFieldIndexList.get(i)];
                currentField.setAccessible(true);
                //从对象中获取相应的属性值
                Object fieldValue = currentField.get(user);
                ps.setObject(i+1,fieldValue);
            }

            int result = ps.executeUpdate();

            if(result==1){
                System.out.println("添加成功");
            }else {
                System.out.println("添加失败");
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            ConnectionTool.closed(conn,ps);
        }
    }

    /**
     * 登录
     * @param username
     * @param password
     * @return
     */
    @Override
    public boolean login(String username, String password) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;


        try {
            //加载数据库驱动
            Class.forName(Const.DRIVER);
            //建立数据库连接
            conn = DriverManager.getConnection(
                    Const.URL_TEST,
                    Const.USERNAME,
                    Const.PASSWORD
            );

            //sql语句查询
            String sql = "select * from TB_USER where username = ? and password = ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1,username);
            ps.setString(2,password);

            // 执行查询
            rs = ps.executeQuery();

            // 如果查询结果有记录，说明用户名和密码正确
            if (rs.next()){
                return true;
            }else {
                return false;
            }


        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            ConnectionTool.closed(conn,rs,ps);
        }
    }

    /**
     * 修改一个用户
     * @param user
     */
    @Override
    public void update(User user) {
        Connection conn = null;
        PreparedStatement ps = null;

        //创建需要添加到表中的属性的索引列表
        List<Integer> toAddFieldIndexList = new ArrayList<>();
        String tableName = "";
        String fieldName = "";
        Object primaryKey = null;

        //获取对象的Class类型
        Class userClass = user.getClass();

        //判断当前类对象对应的类中是否存在Table这个注解
        if (userClass.isAnnotationPresent(Table.class)){
            //如果有，则就把这个注解的value值作为表名
            Table table = (Table) userClass.getDeclaredAnnotation(Table.class);
            tableName = table.value();
        }else{
            //获取当前对象的类名直接作为sql语句的表名
            tableName = userClass.getSimpleName().toLowerCase();
        }

        try {
            Class.forName(Const.DRIVER);
            conn = DriverManager.getConnection(
                    Const.URL_TEST,
                    Const.USERNAME,
                    Const.PASSWORD);

            //update 表名 set 属性名 = ？, 属性名 = ？， ，，，（修改除主键外的值） where 主键 = ?;
            //insert into from 表名(,,,) values (???)
            StringBuffer sb = new StringBuffer("update "+tableName+" set ");

            //获取当前对象的类中所有属性数组
            Field[] fields = userClass.getDeclaredFields();

            //遍历所有的属性
            for (int i = 0; i < fields.length; i++) {
                Field currentField = fields[i];

                //需要先判断该属性是否为主键
                //如果不是主键则再判断它是否有Column注解，如果有就要读取Column注解中的value值作为要添加的字段
                if (!currentField.isAnnotationPresent(PrimaryKey.class)){
                    if (currentField.isAnnotationPresent(Column.class)){
                        Column columnAnnotation = currentField.getDeclaredAnnotation(Column.class);
                        fieldName = columnAnnotation.value();
                    }else {
                        //如果没有就直接读取属性名作为要添加的字段名
                        fieldName = currentField.getName().toLowerCase();
                    }
                    sb.append(fieldName+(i==fields.length-1?"=?":"=?,"));
                    toAddFieldIndexList.add(i);
                }

            }

            //接下来再次循环所有的属性，判断该属性的注解是否为主键
            for (int i = 0; i < fields.length; i++) {
                Field currentField = fields[i];

                //需要先判断该属性是否为主键
                //如果是主键则再判断它是否有Column注解，如果有就要读取Column注解中的value值作为要添加的字段
                if (currentField.isAnnotationPresent(PrimaryKey.class)){
                    if (currentField.isAnnotationPresent(Column.class)){
                        Column columnAnnotation = currentField.getDeclaredAnnotation(Column.class);
                        primaryKey = columnAnnotation.value();
                    }else {
                        //如果没有就直接读取属性名作为要添加的字段名
                        primaryKey = currentField.getName().toLowerCase();
                    }
                    sb.append(" where " + primaryKey + "=?");
                }
                toAddFieldIndexList.add(i);
            }

            //对sql语句进行预处理
            ps = conn.prepareStatement(sb.toString());

            //设置sql语句里的问号值
            for (int i = 0; i < fields.length; i++) {
                //获取属性数组中索引值为toAddFieldIndexList中的第i个索引值
                Field currentField = fields[toAddFieldIndexList.get(i)];
                currentField.setAccessible(true);
                //从对象中获取相应的属性值
                Object fieldValue = currentField.get(user);
                ps.setObject(i+1,fieldValue);
            }

            int result = ps.executeUpdate();
            if(result==1){
                System.out.println("更新成功");
            }else {
                System.out.println("更新失败");
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            ConnectionTool.closed(conn,ps);
        }
    }

    /**
     * 根据用户编号删除多个数据
     * @param ids
     */
    @Override
    public void delete(int[] ids) {
        Connection conn = null;
        PreparedStatement ps = null;

        String tableName = "";
        String fieldName = "";
        Object primaryKey = null;

        //创建User的class类型
        Class clazz = User.class;

        //判断当前类中是否存在Table这个注解
        if (clazz.isAnnotationPresent(Table.class)){
            //如果有，则就把这个注解的value值作为表名
            Table table = (Table) clazz.getDeclaredAnnotation(Table.class);
            tableName = table.value();
        }else{
            //获取当前类名直接作为sql语句的表名
            tableName = clazz.getSimpleName().toLowerCase();
        }

        try {
            Class.forName(test01.demo02.userOperation.tools.Const.DRIVER);
            conn = DriverManager.getConnection(test01.demo02.userOperation.tools.Const.URL_TEST, test01.demo02.userOperation.tools.Const.USERNAME, test01.demo02.userOperation.tools.Const.PASSWORD);

            //delete from 表名 where id=?
            //update 表名 set 属性名 = ？, 属性名 = ？， ，，，（修改除主键外的值） where 主键 = ?;
            //insert into from 表名(,,,) values (???)
            for (int j = 0; j < ids.length; j++) {

                int id = ids[j];

                StringBuffer sb = new StringBuffer("delete from "+tableName+" where ");

                //获取当前类中所有属性数组
                Field[] fields = clazz.getDeclaredFields();

                //循环所有的属性，判断该属性的注解是否为主键
                for (int i = 0; i < fields.length; i++) {
                    Field currentField = fields[i];
                    //需要先判断该属性是否为主键
                    if (currentField.isAnnotationPresent(PrimaryKey.class)){
                        if (currentField.isAnnotationPresent(Column.class)){
                            Column columnAnnotation = currentField.getDeclaredAnnotation(Column.class);
                            primaryKey = columnAnnotation.value();
                        }else {
                            //如果没有就直接读取属性名作为要添加的字段名
                            primaryKey = currentField.getName().toLowerCase();
                        }
                        sb.append(primaryKey + "=?");
                        break;
                    }
                }


                //对sql语句进行预处理
                ps = conn.prepareStatement(sb.toString());

                //设置sql语句里的问号值
                ps.setObject(1,id);

                int result = ps.executeUpdate();
                if(result==1){
                    System.out.println("第"+j+1+"条数据删除成功");
                }else {
                    System.out.println("删除失败");
                }
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            ConnectionTool.closed(conn,ps);
        }
    }

    @Override
    public List<User> getAll() {
        Connection conn = null;
        PreparedStatement ps = null;

        String tableName = "";
        ResultSet rs = null;

        Class clazz = User.class;

        List<User> list = new ArrayList<>();

        //判断当前类中是否存在Table这个注解
        if (clazz.isAnnotationPresent(Table.class)){
            //如果有，则就把这个注解的value值作为表名
            Table table = (Table) clazz.getDeclaredAnnotation(Table.class);
            tableName = table.value();
        }else{
            //获取当前类名直接作为sql语句的表名
            tableName = clazz.getSimpleName().toLowerCase();
        }

        try {
            Class.forName(Const.DRIVER);
            conn = DriverManager.getConnection(Const.URL_TEST, Const.USERNAME, Const.PASSWORD);

            //select * from 表名
            //delete from 表名 where 主键=?
            //update 表名 set 属性名 = ？, 属性名 = ？， ，，，（修改除主键外的值） where 主键 = ?;
            //insert into from 表名(,,,) values (???)
            StringBuffer sb = new StringBuffer("select * from "+tableName);

            //对sql语句进行预处理
            ps = conn.prepareStatement(sb.toString());

            rs = ps.executeQuery();

            //获取结果集的元数据
            ResultSetMetaData rsmd = rs.getMetaData();
            int columnCount = rsmd.getColumnCount();

            //每执行一次while，就是一条元数据集
            while (rs.next()){
                Map<String,Object> map = new HashMap<>();
                for (int i = 1; i <= columnCount; i++) {
                    map.put(rsmd.getColumnName(i),rs.getObject(i));
                }

                /**
                 * 以下这段代码的主要功能是遍历 clazz 类中声明的所有字段，
                 * 然后从 map 中查找与字段名对应的键值对，若存在则将对应的值设置到 user 对象的相应字段上。
                 * 对于 int 类型的字段，会将值转换为 int 类型；对于 String 类型的字段，会将值转换为字符串。
                 */

                //创建一个User类的实例
                User user = new User();
                //遍历clazz类中声明的所有字段
                for (Field field : clazz.getDeclaredFields()) {
                    //设置该字段为可访问，以便可以访问私有字段
                    field.setAccessible(true);
                    //获取当前字段名称
                    String fieldName = field.getName();
                    //检查map中是否包含当前字段名作为键
                    if (map.containsKey(fieldName)) {
                        //如果包含，则从map中获取对应的值
                        Object value = map.get(fieldName);
                        //如果当前字段的类型是int且获取的值不为空
                        if (field.getType() == int.class && value != null) {
                            //将获取的值转为int类型，并设置到user对象的该字段上
                            field.set(user, ((Number) value).intValue());
                        } else if (field.getType() == String.class && value != null) {
                            field.set(user, value.toString());
                        }
                    }
                }
                list.add(user);
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            ConnectionTool.closed(conn, rs, ps);
        }
        return list;
    }

    /**
     * 查询某一页的用户
     * @param pagesize
     * @param pageOrder
     * @return
     */
    @Override
    public List<User> getUserForPage(int pagesize, int pageOrder) {
        Connection conn = null;
        PreparedStatement ps = null;

        String tableName = "";
        ResultSet rs = null;

        Class clazz = User.class;

        List<User> list = new ArrayList<>();

        //判断当前类中是否存在Table这个注解
        if (clazz.isAnnotationPresent(Table.class)){
            //如果有，则就把这个注解的value值作为表名
            Table table = (Table) clazz.getDeclaredAnnotation(Table.class);
            tableName = table.value();
        }else{
            //获取当前类名直接作为sql语句的表名
            tableName = clazz.getSimpleName().toLowerCase();
        }

        try {
            Class.forName(Const.DRIVER);
            conn = DriverManager.getConnection(Const.URL_TEST, Const.USERNAME, Const.PASSWORD);

            //select * from 表名
            //delete from 表名 where 主键=?
            //update 表名 set 属性名 = ？, 属性名 = ？， ，，，（修改除主键外的值） where 主键 = ?;
            //insert into from 表名(,,,) values (???)
            StringBuffer sb = new StringBuffer("select * from "+tableName + " limit " + (pageOrder-1)*2 +"," +pagesize);

            //对sql语句进行预处理
            ps = conn.prepareStatement(sb.toString());

            rs = ps.executeQuery();

            //获取结果集的元数据
            ResultSetMetaData rsmd = rs.getMetaData();
            int columnCount = rsmd.getColumnCount();

            //每执行一次while，就是一条元数据集
            while (rs.next()){
                Map<String,Object> map = new HashMap<>();
                for (int i = 1; i <= columnCount; i++) {
                    map.put(rsmd.getColumnName(i),rs.getObject(i));
                }
                User user = new User();
                for (Field field : clazz.getDeclaredFields()) {
                    field.setAccessible(true);
                    String fieldName = field.getName();
                    if (map.containsKey(fieldName)) {
                        Object value = map.get(fieldName);
                        if (field.getType() == int.class && value != null) {
                            field.set(user, ((Number) value).intValue());
                        } else if (field.getType() == String.class && value != null) {
                            field.set(user, value.toString());
                        }
                    }
                }
                list.add(user);
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            ConnectionTool.closed(conn, rs, ps);
        }
        return list;
    }

    @Override
    public User getUserById(int id) {
        Connection conn = null;
        PreparedStatement ps = null;

        String tableName = "";
        ResultSet rs = null;

        Class clazz = User.class;


        //判断当前类中是否存在Table这个注解
        if (clazz.isAnnotationPresent(Table.class)) {
            //如果有，则就把这个注解的value值作为表名
            Table table = (Table) clazz.getDeclaredAnnotation(Table.class);
            tableName = table.value();
        } else {
            //获取当前类名直接作为sql语句的表名
            tableName = clazz.getSimpleName().toLowerCase();
        }

        try {
            Class.forName(Const.DRIVER);
            conn = DriverManager.getConnection(Const.URL_TEST, Const.USERNAME, Const.PASSWORD);

            //select * from 表名
            //delete from 表名 where 主键=?
            //update 表名 set 属性名 = ？, 属性名 = ？， ，，，（修改除主键外的值） where 主键 = ?;
            //insert into from 表名(,,,) values (???)
            StringBuffer sb = new StringBuffer("select * from " + tableName + " where userno = " + "?");

            //对sql语句进行预处理
            ps = conn.prepareStatement(sb.toString());

            ps.setInt(1,id);

            rs = ps.executeQuery();

            //获取结果集的元数据
            ResultSetMetaData rsmd = rs.getMetaData();
            int columnCount = rsmd.getColumnCount();

            //每执行一次while，就是一条元数据集
            while (rs.next()) {
                Map<String, Object> map = new HashMap<>();
                for (int i = 1; i <= columnCount; i++) {
                    map.put(rsmd.getColumnName(i), rs.getObject(i));
                }
                User user = new User();
                for (Field field : clazz.getDeclaredFields()) {
                    field.setAccessible(true);
                    String fieldName = field.getName();
                    if (map.containsKey(fieldName)) {
                        Object value = map.get(fieldName);
                        if (field.getType() == int.class && value != null) {
                            field.set(user, ((Number) value).intValue());
                        } else if (field.getType() == String.class && value != null) {
                            field.set(user, value.toString());
                        }
                    }
                }
                return user;
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            ConnectionTool.closed(conn, rs, ps);
        }

        return null;
    }
}
